package its_models.tutor_model;

import database_handler.DatabaseManager;
import help_util.DateTime;
import help_util.IndexConverter;
import its_models.expert_model.ExpertModelProperties;
import its_models.expert_model.expert_help_util.StrategyGraphUnit;
import its_models.expert_model.expert_help_util.StepsMatrix;
import its_models.student_model.StudentModelProperties;
import java.util.ArrayList;
import model_controller.ControlLayer;
import its_models.tutor_model.tutor_help_util.AutoHintsTimerHandler;
import sudoku_properties.BasicSudokuParam;
import sudoku_properties.SudokuGameInfoParam;


/************************************************************
 * Class HintsGenerator 
 * 1. provide hints to learner 
 ************************************************************/
public class HintsGenerator {

    /* Consturctor */
    public HintsGenerator(ControlLayer inputController){
        Controller = inputController;
        rulesRanking = new ArrayList<Integer> ();
        /* default rules ranking */
        for(int i=0; i<LOGICAL_RULES.length; i++){
            rulesRanking.add(i);
        }
        isCorrectCell = new boolean [NUM_CELLS_IN_UNIT][NUM_CELLS_IN_UNIT];
        /* default all as true */
        for(int i=0; i<NUM_CELLS_IN_UNIT; i++){
            for(int j=0; j<NUM_CELLS_IN_UNIT; j++){
                isCorrectCell[i][j] = true;
            }
        }

        // 20091112 phrase2 start
        autoHintsTimer = new AutoHintsTimerHandler(Controller);
        dbManager = new DatabaseManager(Controller);
        // 20091112 phrase2 end

        //20100112
        LangMode = Controller.getLangMode();
    }

    /* solved puzzle  - for answer checking */
    public void setup(StrategyGraphUnit[][] inputSolvedPuzzle){
        solvedPuzzle = new StrategyGraphUnit[NUM_CELLS_IN_UNIT][NUM_CELLS_IN_UNIT];
        for(int i=0; i<inputSolvedPuzzle.length; i++){
            for(int j=0; j<inputSolvedPuzzle[i].length; j++){
                solvedPuzzle[i][j] = new StrategyGraphUnit();
                solvedPuzzle[i][j].copy(inputSolvedPuzzle[i][j]);
            }
        }
    }

    /* logical Rules Found On -  giving hints */
    public void setupStepsMatrix(StepsMatrix inputStepsMatrix){
       stepsMatrix = new StepsMatrix(Controller);
       stepsMatrix.copy(inputStepsMatrix);
    }

    // 20091018 phrase2 start
    public String getAllLogicalRule(int indexX, int indexY){
        StringBuffer allRule = new StringBuffer();
        for(int i=0; i < stepsMatrix.getBreadthStepsLength(); i++){
            for(int j=0; j<stepsMatrix.getBreadthStepsFoundOnRule(i).size(); j++){
                if(stepsMatrix.getBreadthStepsFoundOnRule(i).get(j).getColumnIndex() == indexX &&
                        stepsMatrix.getBreadthStepsFoundOnRule(i).get(j).getRowIndex() == indexY){
                    allRule.append(stepsMatrix.getBreadthStepsFoundOnRule(i).get(j).getLogicalRule());
                    allRule.append(" "+ RULE_SCOPE_SEPARATOR +" ");
                    allRule.append(stepsMatrix.getBreadthStepsFoundOnRule(i).get(j).getScopeEffectiveIn());
                    allRule.append(" "+ HINTS_SEPARATOR +" ");
                }
            }
        }
        return allRule.toString();
    }
    // 20091018 phrase2 end
    /* Rules Ranking */
    public void setRulesRanking(ArrayList<Integer> inputRulesRanking){
        rulesRanking.clear();
        rulesRanking = inputRulesRanking ;
    }

    /* get Hints */
    /* check the value and return if all input true */
    public boolean isLearnerInputCorrect(int inputIndexX, int inputIndexY, int inputCellValue){
        /* record input true or false */
        // 20091019 phrase2 start
        if(inputCellValue == 0 ){
            /* make sure user cannot remove correct input */
            //Controller.setTextLearnerStepsMsgBox(" result: " + !Controller.getDefiniteValueSet(inputIndexX, inputIndexY)+"\n");

            // isCorrectCell[inputIndexX][inputIndexY] = !(Controller.getDefiniteValueSet(inputIndexX, inputIndexY));
            if(Controller.getDefiniteValueSet(inputIndexX, inputIndexY)){
                return false;
            }else{
                isCorrectCell[inputIndexX][inputIndexY] = true;
            }
        }
        //if(inputCellValue == 0  || solvedPuzzle[inputIndexX][inputIndexY].getDefiniteValue() == inputCellValue){
        // 20091019 phrase2 end
        else if(solvedPuzzle[inputIndexX][inputIndexY].getDefiniteValue() == inputCellValue){
            isCorrectCell[inputIndexX][inputIndexY] = true;
        }
        else{
            isCorrectCell[inputIndexX][inputIndexY] = false;
        }
        return isCorrectCell[inputIndexX][inputIndexY];
    }
    public boolean isLearnerInputAllCorrect(){
        /* check total puzzles true or false */

        for(int i=0; i<NUM_CELLS_IN_UNIT; i++){
            for(int j=0; j<NUM_CELLS_IN_UNIT; j++){
                if(!isCorrectCell[i][j]){
                    return false;
                }
            }
        }
        return true;
     }
    /* calu cells are only for user convenience */

    /* get Hints Start *****************************************************************************/
    public String getHints(int inputHintsLevel){

        final int hintsLevel = inputHintsLevel;
        StrategyGraphUnit hintsCell = new StrategyGraphUnit();
        hintsCell.copy(this.getLogicalRulesFound());
        String hints = "";

        if(hintsCell!=null){
            /* 0: row 1: column 2: rules 3: answer with premises */
            if(hintsLevel==0){
                hints = this.getRowHints(hintsCell);
            }else if (hintsLevel==1){
                hints = this.getColumnHints(hintsCell);
            }else if (hintsLevel==2){
                hints = this.getRulesHints(hintsCell);
            }else if (hintsLevel==3){
                hints = this.getAnswerHints(hintsCell);
                if(hintsCell.getPurposeOfLastLogicalRule().equalsIgnoreCase(ExpertModelProperties.getDefineIndicator())){
                    Controller.setCellValueBySystem(hintsCell.getColumnIndex(), hintsCell.getRowIndex(), hintsCell.getDefiniteValue(), ASSIGN_VALUE_COMMAND, true);
                }else if(hintsCell.getPurposeOfLastLogicalRule().equalsIgnoreCase(ExpertModelProperties.getEliminateIndicator())){
                    final int[] involvedValues = Controller.getInvolvedValue(hintsCell);
                    // 20100126 for loop
                    for(int i=0; i<involvedValues.length; i++){
                        Controller.setVisibleCaluCellsLabel(hintsCell.getColumnIndex(), hintsCell.getRowIndex(), involvedValues[i]-1, false);
                        Controller.setCellElminBySystem(hintsCell, involvedValues[i], 1);
                    }
                }
            }
        }
        return hints;
    }

    // 20091112 phrase2 start
    public boolean isElminHintsCell(StrategyGraphUnit checkUnit){
        boolean isHintsCellFound = false;
        for(int i=0; i<this.rulesRanking.size(); i++){
              
            /* check if apply elimination rules*/
            if((this.rulesRanking.get(i) == ExpertModelProperties.getIntersectReductIndex()) ||
                    (this.rulesRanking.get(i) == ExpertModelProperties.getNakedPairsIndex()) ||
                    (this.rulesRanking.get(i) == ExpertModelProperties.getHiddenPairsIndex()) ){

                for(int j=0; j<stepsMatrix.getBreadthStepsFoundOnRule(this.rulesRanking.get(i)).size(); j++){

                    if(stepsMatrix.getBreadthStepsFoundOnRule(this.rulesRanking.get(i)).get(j).getColumnIndex() == checkUnit.getColumnIndex() &&
                        stepsMatrix.getBreadthStepsFoundOnRule(this.rulesRanking.get(i)).get(j).getRowIndex() == checkUnit.getRowIndex()){
                        isHintsCellFound = true;
                        return isHintsCellFound;
                    }
                }

            }

        }
        return isHintsCellFound;
    }
    
    public StrategyGraphUnit getElminHintsCell(StrategyGraphUnit checkUnit){
        for(int i=0; i<this.rulesRanking.size(); i++){
            /* check if apply elimination rules*/
            if((this.rulesRanking.get(i) == ExpertModelProperties.getIntersectReductIndex()) ||
                    (this.rulesRanking.get(i) == ExpertModelProperties.getNakedPairsIndex()) ||
                    (this.rulesRanking.get(i) == ExpertModelProperties.getHiddenPairsIndex()) ){
                    
                for(int j=0; j<stepsMatrix.getBreadthStepsFoundOnRule(this.rulesRanking.get(i)).size(); j++){
                    if(stepsMatrix.getBreadthStepsFoundOnRule(this.rulesRanking.get(i)).get(j).getColumnIndex() == checkUnit.getColumnIndex() &&
                        stepsMatrix.getBreadthStepsFoundOnRule(this.rulesRanking.get(i)).get(j).getRowIndex() == checkUnit.getRowIndex()){
                        return stepsMatrix.getBreadthStepsFoundOnRule(this.rulesRanking.get(i)).get(j);
                    }
                }
                        
            }
        }
        return null;
    }
    // 20091112 phrase2 end
    

    private StrategyGraphUnit getLogicalRulesFound(){

        StrategyGraphUnit involvedUnit = new StrategyGraphUnit();
        boolean isRuleGet = false;
        for(int i=0; i<rulesRanking.size() && !isRuleGet; i++){
            /* contain hints of specific rule */
            int firstRecord = 0;
            if(stepsMatrix.getBreadthStepsFoundOnRule(rulesRanking.get(i)).size()!=0){
                 involvedUnit.copy(stepsMatrix.getBreadthStepsFoundOnRule(rulesRanking.get(i)).get(firstRecord));
                 final String purposeOfUsed = involvedUnit.getPurposeOfLastLogicalRule();

                 /* if eliminate rule */
                 if(purposeOfUsed.equalsIgnoreCase(ExpertModelProperties.getEliminateIndicator())){
                     while(!isRuleGet && firstRecord < stepsMatrix.getBreadthStepsFoundOnRule(rulesRanking.get(i)).size()){
                         involvedUnit.copy(stepsMatrix.getBreadthStepsFoundOnRule(rulesRanking.get(i)).get(firstRecord));
                         final int indexX = involvedUnit.getColumnIndex();
                         final int indexY = involvedUnit.getRowIndex();

                         final int[] involvedValues = Controller.getInvolvedValue(involvedUnit);

                         for(int j=0; j<involvedValues.length && !isRuleGet; j++){
                             if(Controller.isVisibleCaluCellsLabel(indexX, indexY, involvedValues[j]-1)){
                                 isRuleGet = true;

                             }
                         }
                         firstRecord++;
                     }
                 } /* if define rules */
                 else{
                     isRuleGet = true;
                 }
            }
        }
        return involvedUnit;
    }

    private String getRowHints(StrategyGraphUnit hintsCell){
        final int rowIndexValue = hintsCell.getRowIndex() + 1;
        StringBuffer displayStr =new StringBuffer();

        if(LangMode == ENG_MODE){
            displayStr.append("\nHints generated by System :\n");
            displayStr.append("Please check Row :\t");
            displayStr.append(Integer.toString(rowIndexValue));
            displayStr.append("\n");
        }

        if(LangMode == CHIN_MODE){
            displayStr.append("\n提示\n");
            displayStr.append("請檢查行 :\t");
            displayStr.append(Integer.toString(rowIndexValue));
            displayStr.append("\n");
        }

        return displayStr.toString();
    }
    private String getColumnHints(StrategyGraphUnit hintsCell){
        final int columnIndexValue = hintsCell.getColumnIndex() + 1;
        StringBuffer displayStr =new StringBuffer();
        if(LangMode == ENG_MODE){
            displayStr.append("\nHints generated by System :\n");
            displayStr.append("Please check Column :\t");
            displayStr.append(COLUMN_INDEX_ALPHA[columnIndexValue]);
            displayStr.append("\n");
        }

        if(LangMode == CHIN_MODE){
            displayStr.append("\n提示 :\n");
            displayStr.append("請檢查列 :\t");
            displayStr.append(COLUMN_INDEX_ALPHA[columnIndexValue]);
            displayStr.append("\n");
        }

        return displayStr.toString();
    }
    private String getRulesHints(StrategyGraphUnit hintsCell){
        final String logicalRule = hintsCell.getLogicalRule();
        StringBuffer displayStr =new StringBuffer();
        
        if(LangMode == ENG_MODE){
            displayStr.append("\nHints generated by the system :\n");
            displayStr.append("Please check Rule :\t");
            displayStr.append(logicalRule);
            displayStr.append("\n");
        }

        if(LangMode == CHIN_MODE){
            displayStr.append("\n提示 :\n");
            displayStr.append("請檢查應用的規則 :\t");
            int index = SudokuGameInfoParam.getLogicalRulesIndex(logicalRule);
            displayStr.append(LOGICAL_RULES_IN_CHIN[index]);
            displayStr.append("\n");
        }
        return displayStr.toString();
    }
    private String getAnswerHints(StrategyGraphUnit hintsCell){
        final int indexX = hintsCell.getColumnIndex();
        final int indexY = hintsCell.getRowIndex();

        final int columnIndexValue = hintsCell.getColumnIndex() + 1;
        final int rowIndexValue = hintsCell.getRowIndex() + 1;
        final String logicalRule = hintsCell.getLogicalRule();


        final int[] valuesInvolved = Controller.getInvolvedValue(hintsCell);
        final String purposeOfLastUsed= hintsCell.getPurposeOfLastLogicalRule();

        // 20100126 add comment
        /*StringBuffer valuesInvolvedBuffer = new StringBuffer();
        for(int i=0; i<valuesInvolved.length; i++){
            valuesInvolvedBuffer.append(valuesInvolved[i]);
            valuesInvolvedBuffer.append(" ");
        }*/

        StringBuffer displayStr =new StringBuffer();
        if(LangMode ==ENG_MODE){
            displayStr.append("\nPermises :\n");
            if(logicalRule.equalsIgnoreCase(ExpertModelProperties.getSingleInGroupIndicator())){
                displayStr.append(Controller.getSGSolvingProcedure(hintsCell));
            }else if(logicalRule.equalsIgnoreCase(ExpertModelProperties.getNakedSingleIndicator())){
                displayStr.append(Controller.getNSSolvingProcedure(hintsCell));
            }else if(logicalRule.equalsIgnoreCase(ExpertModelProperties.getHiddenSingleIndicator())){
                displayStr.append(Controller.getHSSolvingProcedure(hintsCell));
            }else if(logicalRule.equalsIgnoreCase(ExpertModelProperties.getIntersectReductIndicator())){
                displayStr.append(Controller.getIRSolvingProcedure(hintsCell, valuesInvolved));
            }else if(logicalRule.equalsIgnoreCase(ExpertModelProperties.getNakedPairsIndicator())){
                displayStr.append(Controller.getNPSolvingProcedure(hintsCell, valuesInvolved));
            }else if(logicalRule.equalsIgnoreCase(ExpertModelProperties.getHiddenPairsIndicator())){
                displayStr.append(Controller.getHPSolvingProcedure(hintsCell, valuesInvolved));
            }
        }
        if(LangMode == CHIN_MODE){
            displayStr.append("\n提示 :\n");
            if(logicalRule.equalsIgnoreCase(ExpertModelProperties.getSingleInGroupIndicator())){
                displayStr.append(Controller.getSGSolvingProcedureInChinese(hintsCell));
            }else if(logicalRule.equalsIgnoreCase(ExpertModelProperties.getNakedSingleIndicator())){
                displayStr.append(Controller.getNSSolvingProcedureInChinese(hintsCell));
            }else if(logicalRule.equalsIgnoreCase(ExpertModelProperties.getHiddenSingleIndicator())){
                displayStr.append(Controller.getHSSolvingProcedureInChinese(hintsCell));
            }else if(logicalRule.equalsIgnoreCase(ExpertModelProperties.getIntersectReductIndicator())){
                displayStr.append(Controller.getIRSolvingProcedureInChinese(hintsCell, valuesInvolved));
            }else if(logicalRule.equalsIgnoreCase(ExpertModelProperties.getNakedPairsIndicator())){
                displayStr.append(Controller.getNPSolvingProcedureInChinese(hintsCell, valuesInvolved));
            }else if(logicalRule.equalsIgnoreCase(ExpertModelProperties.getHiddenPairsIndicator())){
                displayStr.append(Controller.getHPSolvingProcedureInChinese(hintsCell, valuesInvolved));
            }
        }

        displayStr.append("\n");
        return displayStr.toString();
    }
    /* get hints end *******************************************************************************/

    // 20091112 phrase2 start
    /* Automatic hints giving start ****************************************************************/
    
    public void reStartAutoHintsTimer(int hintsLevel){
        int timeSlot = 0;
        for(int i=0; i<DIFFICULTY_LEVEL.length; i++){
            if(Controller.getGameDifficulty().equalsIgnoreCase(DIFFICULTY_LEVEL[i])){
                timeSlot = dbManager.getAutoHintsTimeSlot(i);
                if(timeSlot == 0){
                    dbManager.recordError(this.getClass().getName().toString(), "No Time Slot is get", DateTime.getTimestamp());
                }
            }
        }
        autoHintsTimer.setTimeSlot(timeSlot);
        autoHintsTimer.setHintsLevel(hintsLevel);
        autoHintsTimer.reStartTimer();
    }

    public void setAutoHintsTimerOff(){
        autoHintsTimer.setTimerOff();
    }

    public void isEnableAutoHints(boolean isEnabled){
        autoHintsTimer.setAutoHitnsEnabled(isEnabled);
    }

    // 20100126
    public ArrayList<Integer> getRulesRanking(){
        return rulesRanking;
    }
    /* Automatic hints giving end ******************************************************************/
    // 20091112 phrase2 end

    /* Single in Group */
    /* Naked Single */
    /* Hidden Single */
    /* Intersecton Reduction*/

    /* local variables */
    private ControlLayer Controller;
    /* to store the ans generated by expert */
    private StrategyGraphUnit[][] solvedPuzzle;
    /* to store the current hints that can gave */
    //private ArrayList<CellValuePossibilityUnit>[] breadthSteps;
    private StepsMatrix stepsMatrix;
    /* rules priority ranking - information are added in order */
    private ArrayList<Integer> rulesRanking;
    /* store the correctness of the game */
    private boolean[][] isCorrectCell;

    // 20091112 phrase2 start
    private AutoHintsTimerHandler autoHintsTimer;
    private DatabaseManager dbManager;
    // 20091112 phrase2 end

    //20100112
    private int LangMode;

    /* Constant variables */
    /* SudokuGameInfoParam */
    final static String[] LOGICAL_RULES = SudokuGameInfoParam.getLogicalRules();
    final static String[] LOGICAL_RULES_IN_CHIN = SudokuGameInfoParam.getLogicalRulesInChin();
    final String[] HINTS_LEVEL = SudokuGameInfoParam.getHintsLevel();
    // 20091018 phrase2 start
    final String HINTS_SEPARATOR = SudokuGameInfoParam.getHintsSeparator();
    final String RULE_SCOPE_SEPARATOR = TutorModelProperties.getRuleScopeSeparator();
    // 20091018 phrase2 end
    final String[] COLUMN_INDEX_ALPHA = SudokuGameInfoParam.getColumnIndexAlpha();
    final static int ENG_MODE = SudokuGameInfoParam.getEnglishMode();
    final static int CHIN_MODE = SudokuGameInfoParam.getChineseMode();
    // 20091112 phrase2 start
    final static String[] DIFFICULTY_LEVEL = SudokuGameInfoParam.getDifficultyLevels();
    // 20091112 phrase2 end
    /* BasicSudokuParam */
    final static int NUM_CELLS_IN_UNIT = BasicSudokuParam.getNumCellsInUnit();
    /* TutorModelProperties */
    final static int[] AUTO_HINTS_TIMING = TutorModelProperties.getAutoHintsTiming();
    /* studentModelProperties */
    final String ASSIGN_VALUE_COMMAND = StudentModelProperties.getAssignValueCommand();

}
